﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Collections;

namespace LoadGenerator
{
    public class ByteBuffer
    {
        int _incrementSz;           //increment by this size
        int _currentLocation = 0;   //the current location till which written
        int _totalLen;              //stores the total length
        Object _associated;         //strange that this shd be present here I know, but ease of use

        ArrayList _buckets = new ArrayList();   //different buckets expanded in this buffer

        /**
         * Construct a buffer with an initial size of sz 
         * and increment everytime by sz bytes.
         * */
        public ByteBuffer(int sz)
        {
            _incrementSz = sz;
            _totalLen = 0;
            resize();
        }

        //helper function to resize the bytes stored in the buffer
        private byte[] resize()
        {
            byte[] data = new byte[_incrementSz];
            _buckets.Add(data);
            return data;
        }

        //helper function to retrieve the lastest buffer in which the latest
        //is stored
        private byte[] getLatest()
        {
            return (byte[])_buckets[_buckets.Count - 1];
        }

        //append the given bytes to the buffer from the startloc to startloc+len
        public void Append(byte[] b, int startloc, int len)
        {
            byte[] data = getLatest();
            for (int cnt = startloc; cnt < len; cnt++)
            {
                if (_currentLocation >= _incrementSz)
                {
                    data = resize();
                    _currentLocation = 0;
                }
                data[_currentLocation] = b[cnt];
                _currentLocation++;
                _totalLen++;
            }
        }

        //replace the data from index to count with b
        public void Replace(byte[] b, int index, int count)
        {
            if (b.Length < count) throw new Exception("Bytes provided is not sufficient");
            int bucket = (index / _incrementSz);
            int position = (index % _incrementSz); //remainder
            byte[] bucketbytes = (byte[])_buckets[bucket];
            int cnt = 0;
            while (cnt < count)
            {
                bucketbytes[position] = b[cnt];
                position++;
                if (position > _incrementSz)
                {
                    position = 0;
                    bucket++;
                    bucketbytes = (byte[])_buckets[bucket];
                }
                cnt++;
            }
        }

        /**
         * retrieve the data stored in this buffer as a single byte array.
         * */
        public byte[] getData()
        {
            int length = (_buckets.Count - 1) * _incrementSz; //all previous buffers r full
            length += _currentLocation;
            byte[] data = new byte[length];

            int cnt = 0;
            int blen = _buckets.Count;
            for (int i = 0; i < blen; i++)
            {
                byte[] curr = (byte[])_buckets[i];
                int len = (i == (blen - 1)) ? _currentLocation : _incrementSz;
                for (int j = 0; j < len; j++)
                {
                    data[cnt] = curr[j];
                    cnt++;
                }
            }

            return data;
        }

        public int Length { get { return _totalLen; } }

        public void clear()
        {
            //retain the allocated bytes. will be reused.
            _currentLocation = 0;
            _totalLen = 0;
            _buckets.Clear();
            resize();
        }

        public void truncate(int len)
        {
            byte[] bytes = getData();
            clear();
            Append(bytes, len, (bytes.Length - len));
        }

        public Object associated { get { return _associated; } set { _associated = value; } }
    }
}
